《逆向工程核心原理》学习笔记(四):API钩取 | 您所在的位置:网站首页 › iat hook原理 › 《逆向工程核心原理》学习笔记(四):API钩取 |
目录
前言一、API钩取简介1、基础概念2、技术图表
二、调试钩取技术1、调试器2、调试流程3、示例:记事本 WriteFile() API钩取(1)hookdbg.exe(2)试验
三、DLL注入实现IAT钩取技术1、选定目标API2、IAT钩取工作原理3、示例:计算器显示中文数字(1)hookiat.dll(2)InjectDll.exe
四、API代码修改技术1、API代码修改原理2、示例:隐藏进程(1)相关API(2)源码
五、全局API钩取1、相关API(1)Kernel32.CreateProcess()(2)Ntdll.ZwResumeThread()
2、源码(1)HideProc2.exe(2)stealth2.dll
结语
前言
继续学习《逆向工程核心原理》,本篇笔记是第四部分:API钩取,主要介绍了调试钩取、DLL注入实现IAT钩取、API代码修改钩取和全局API钩取等内容 一、API钩取简介 1、基础概念钩取(Hook):截取信息、更改程序执行流向、添加新功能的技术 使用反汇编/调试器把握程序结构与原理开发Hook代码,以修改bug、改善程序功能灵活操作可执行文件和进程内存,设置Hook代码API(Application Programming Interface):可以认为是调用资源的路径,notepad.exe为例如下图所示
通过调试钩取技术,来钩取kernel32!WriteFile() API 1、调试器调试器(Debbuger):能逐一执行被调试者的指令,拥有对寄存器和内存的所有访问权限,工作原理如下图所示
基本思路:被调试者的API起始部份修改为0xCC,控制权转移到调试器后执行指定操作,最后使被调试者重新进入运行状态 对目标进程进行附加操作,使之成为被调试者Hook:API起始地址的第1个字节修改为0xCC调用相应API,控制权转移到调试器执行操作:操作参数、返回值等脱钩:0xCC恢复原值运行相应API(正常状态)Hook:再次修改为0xCC(继续钩取)控制权返还被调试者 3、示例:记事本 WriteFile() API钩取目标是将notepad.exe中所有小写字母都变成大写字母 WriteFile() 定义如下: BOOL WriteFile( HANDLE hFile, //文件句柄 LPCVOID lpBuffer, //数据缓存区指针 DWORD nNumberOfBytesToWrite, //要写的字节数 LPDWORD lpNumberOfBytesWritten, //用于保存实际写入字节数的存储区域的指针 LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针 ); (1)hookdbg.exe源码如下: // hookdbg.exe #include "windows.h" #include "stdio.h" LPVOID g_pfWriteFile = NULL; CREATE_PROCESS_DEBUG_INFO g_cpdi; BYTE g_chINT3 = 0xCC, g_chOrgByte = 0; BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) { // 获取 WriteFile() API 地址(注意是调试进程的内存地址,不是被调试进程) g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile"); // API Hook - WriteFile() // 更改第一个字节为 0xCC (INT 3) // (orginal byte 是g_chOrgByte备份) memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL); WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chINT3, sizeof(BYTE), NULL); return TRUE; } BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde) { CONTEXT ctx; PBYTE lpBuffer = NULL; DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i; PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord; // 断点异常 (INT 3) if( EXCEPTION_BREAKPOINT == per->ExceptionCode ) { // 断点地址为 WriteFile() API 地址 if( g_pfWriteFile == per->ExceptionAddress ) { // #1. Unhook // 0xCC 恢复为 original byte WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL); // #2. 获取线程上下文 ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(g_cpdi.hThread, &ctx); // #3. 获取 WriteFile() 的 param 2, 3 值 // 函数参数存在于相应进程的栈 // param 2 : ESP + 0x8 缓冲区地址 // param 3 : ESP + 0xC 缓冲区大小 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8), &dwAddrOfBuffer, sizeof(DWORD), NULL); ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC), &dwNumOfBytesToWrite, sizeof(DWORD), NULL); // #4. 分配临时缓冲区 lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1); memset(lpBuffer, 0, dwNumOfBytesToWrite+1); // #5. 复制 WriteFile() 缓冲区到临时缓冲区 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); printf("\n### original string ###\n%s\n", lpBuffer); // #6. 小写字母 -> 大写字母 for( i = 0; i DEBUG_EVENT de; DWORD dwContinueStatus; // 等待被调试者发生事件 while( WaitForDebugEvent(&de, INFINITE) ) { dwContinueStatus = DBG_CONTINUE; //dwContinueStatus值为DBG_CONTINUE(处理正常)或DBG_EXCEPTION_NOT_HANDLED(无法处理或在SEH中处理) // 被调试者生成或附加事件 if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode ) { OnCreateProcessDebugEvent(&de); //OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄 } // 异常事件 else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode ) { if( OnExceptionDebugEvent(&de) ) //OnExceptionDebugEvent是EXCEPTION_DEBUG_EVENT事件句柄,处理被调试者的INT3指令 continue; } // 被调试者终止事件 else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode ) { // 被调试者终止 -> debugger 终止 break; } // 再次运行被调试者 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus); } } int main(int argc, char* argv[]) { DWORD dwPID; if( argc != 2 ) { printf("\nUSAGE : hookdbg.exe \n"); return 1; } // Attach Process dwPID = atoi(argv[1]); //以程序运行参数的形式接收进程PID if( !DebugActiveProcess(dwPID) ) //将调试器附加到进程上 { printf("DebugActiveProcess(%d) failed!!!\n" "Error Code = %d\n", dwPID, GetLastError()); return 1; } // 调试器循环 DebugLoop(); return 0; } (2)试验先运行notepad.exe,并获取notepad的PID是32220,然后运行hookdbg.exe,如下 本节向计算器calc.exe插入用户的DLL文件,钩取IAT的user32.SetWindowTextW() API地址,使得计算器显示中文数字 1、选定目标APIPEView打开calc.exe,在IAT中寻找API,如下两个负责显示文本 在OD里验证下 右键-search for-all intermodular calls,在SetWindowTextW()设置断点
关于IAT,可以见《逆向工程核心原理》学习笔记(二):PE文件 原理如下图所示: 首先注入hookiat.dll文件,文件中提供了 MySetWindowTextW() 函数然后修改IAT中的CALL的值为 MySetWindowTextW() 函数起始地址一系列处理后,再CALL到user32.SetWindowTextW()函数起始地址user32.SetWindowTextW()执行完后返回到hookiat.dll执行下一条指令最后返回到01002628类似于《逆向工程核心原理》学习笔记(三):DLL注入 // InjectDll.exe #include "stdio.h" #include "windows.h" #include "tlhelp32.h" #include "winbase.h" #include "tchar.h" void usage() { printf("\nInjectDll.exe by ReverseCore\n" "- blog : http://www.reversecore.com\n" "- email : [email protected]\n\n" "- USAGE : InjectDll.exe \n\n"); } BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName) { HANDLE hProcess, hThread; LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR); LPTHREAD_START_ROUTINE pThreadProc; if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { DWORD dwErr = GetLastError(); return FALSE; } pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL); pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName) { BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) ) return FALSE; bMore = Module32First(hSnapshot, &me); for( ;bMore ;bMore = Module32Next(hSnapshot, &me) ) { if( !_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName) ) { bFound = TRUE; break; } } if( !bFound ) { CloseHandle(hSnapshot); return FALSE; } if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { CloseHandle(hSnapshot); return FALSE; } pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE; } DWORD _EnableNTPrivilege(LPCTSTR szPrivilege, DWORD dwState) { DWORD dwRtn = 0; HANDLE hToken; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { LUID luid; if (LookupPrivilegeValue(NULL, szPrivilege, &luid)) { BYTE t1[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; BYTE t2[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; DWORD cbTP = sizeof(TOKEN_PRIVILEGES) + sizeof (LUID_AND_ATTRIBUTES); PTOKEN_PRIVILEGES pTP = (PTOKEN_PRIVILEGES)t1; PTOKEN_PRIVILEGES pPrevTP = (PTOKEN_PRIVILEGES)t2; pTP->PrivilegeCount = 1; pTP->Privileges[0].Luid = luid; pTP->Privileges[0].Attributes = dwState; if (AdjustTokenPrivileges(hToken, FALSE, pTP, cbTP, pPrevTP, &cbTP)) dwRtn = pPrevTP->Privileges[0].Attributes; } CloseHandle(hToken); } return dwRtn; } int _tmain(int argc, TCHAR* argv[]) { if( argc != 4 ) { usage(); return 1; } // adjust privilege _EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED); // InjectDll.exe if( !_tcsicmp(argv[1], L"i") ) InjectDll((DWORD)_tstoi(argv[2]), argv[3]); else if(!_tcsicmp(argv[1], L"e") ) EjectDll((DWORD)_tstoi(argv[2]), argv[3]); return 0; } 四、API代码修改技术API代码修改技术:库文件被加载到进程内存后,在目录映像中直接修改要钩取的API代码本身(相较于上一节的IAT钩取,本技术可以钩取更多的不在IAT的API) 本节将用API代码修改技术实现隐藏进程 1、API代码修改原理原始状态如下: 钩取原理如下图所示: 注入stealth.dll,钩取ntdll.ZwQuerySystemInformation() APIntdll.ZwQuerySystemInformation() API起始地址的5个字节代码改为stealth.MyZwQuerySystemInformation() 的地址JMP 10001120于是 ntdll.ZwQuerySystemInformation()被调用的时候,会跳转到stealth.MyZwQuerySystemInformation()1000116A的指令将 ntdll.ZwQuerySystemInformation() API起始地址的5个字节代码恢复原值1000119B的指令调用正常的 ntdll.ZwQuerySystemInformation()ntdll.ZwQuerySystemInformation()执行完后,返回stealth.dll然后10001212再次钩取ntdll.ZwQuerySystemInformation()(即第二步)stealth.MyZwQuerySystemInformation() 执行完后,返回进程一般获取进程快照,用的是CreateToolHelp32Snapshot() API 枚举进程则用的是EnumProcesses() API 而这两个API底层都调用的是 ntdll.ZwQuerySystemInformation() API ntdll.ZwQuerySystemInformation() API可以获取运行中的所有进程信息,形成链表 NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );隐藏进程时,需要钩取所有进程的ZwQuerySystemInformation() API,即全局钩取 (2)源码HideProc.exe,可以认为是InjectDll.exe的加强版 // HideProc.exe #include "windows.h" #include "stdio.h" #include "tlhelp32.h" #include "tchar.h" typedef void (*PFN_SetProcName)(LPCTSTR szProcName); enum {INJECTION_MODE = 0, EJECTION_MODE}; BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; HANDLE hToken; LUID luid; if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ) { printf("OpenProcessToken error: %u\n", GetLastError()); return FALSE; } if( !LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid) ) // receives LUID of privilege { printf("LookupPrivilegeValue error: %u\n", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if( bEnablePrivilege ) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if( !AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); return FALSE; } if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ) { printf("The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath) { HANDLE hProcess, hThread; LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR); LPTHREAD_START_ROUTINE pThreadProc; if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { printf("OpenProcess(%d) failed!!!\n", dwPID); return FALSE; } pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL); pThreadProc = (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath) { BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) ) return FALSE; bMore = Module32First(hSnapshot, &me); for( ; bMore ; bMore = Module32Next(hSnapshot, &me) ) { if( !_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath) ) { bFound = TRUE; break; } } if( !bFound ) { CloseHandle(hSnapshot); return FALSE; } if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { CloseHandle(hSnapshot); return FALSE; } pThreadProc = (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE; } BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath) { DWORD dwPID = 0; HANDLE hSnapShot = INVALID_HANDLE_VALUE; PROCESSENTRY32 pe; // Get the snapshot of the system pe.dwSize = sizeof( PROCESSENTRY32 ); hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL ); // find process Process32First(hSnapShot, &pe); do { dwPID = pe.th32ProcessID; if( dwPID printf("\n Usage : HideProc.exe "\ " \n\n"); return 1; } // change privilege SetPrivilege(SE_DEBUG_NAME, TRUE); // load library hLib = LoadLibrary(argv[3]); // set process name to hide SetProcName = (PFN_SetProcName)GetProcAddress(hLib, "SetProcName"); SetProcName(argv[2]); // Inject(Eject) Dll to all process if( !_tcsicmp(argv[1], L"-show") ) nMode = EJECTION_MODE; InjectAllProcess(nMode, argv[3]); // free library FreeLibrary(hLib); return 0; }stealth.dll // stealth.dll #include "windows.h" #include "tchar.h" #define STATUS_SUCCESS (0x00000000L) typedef LONG NTSTATUS; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 } SYSTEM_INFORMATION_CLASS; typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; BYTE Reserved1[48]; PVOID Reserved2[3]; HANDLE UniqueProcessId; PVOID Reserved3; ULONG HandleCount; BYTE Reserved4[4]; PVOID Reserved5[11]; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved6[6]; } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION) (SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); #define DEF_NTDLL ("ntdll.dll") #define DEF_ZWQUERYSYSTEMINFORMATION ("ZwQuerySystemInformation") // global variable (in sharing memory) #pragma comment(linker, "/SECTION:.SHARE,RWS") #pragma data_seg(".SHARE") TCHAR g_szProcName[MAX_PATH] = {0,}; #pragma data_seg() // global variable BYTE g_pOrgBytes[5] = {0,}; //此函数用来将api前5个字节改为jmp xxxx,以实现钩取 //szDllName:dll名称,szFuncName:api名称,pfnNew:勾取函数地址,pOrgBytes:存储原来5个字节的缓冲区 BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes) { FARPROC pfnOrg; DWORD dwOldProtect, dwAddress; BYTE pBuf[5] = {0xE9, 0, }; PBYTE pByte; // 获取需要勾取的API地址 pfnOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName); pByte = (PBYTE)pfnOrg; // 若已被勾取,则返回False if( pByte[0] == 0xE9 ) return FALSE; // 为了修改5字节,先向内存添加“写”的属性 VirtualProtect((LPVOID)pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 备份原有代码(5字节) memcpy(pOrgBytes, pfnOrg, 5); // 计算 JMP 地址 (E9 XXXX) // => XXXX = pfnNew - pfnOrg - 5 dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5; memcpy(&pBuf[1], &dwAddress, 4); // Hook - 修改 5 byte (JMP XXXX) memcpy(pfnOrg, pBuf, 5); // 恢复内存属性 VirtualProtect((LPVOID)pfnOrg, 5, dwOldProtect, &dwOldProtect); return TRUE; } BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes) { FARPROC pFunc; DWORD dwOldProtect; PBYTE pByte; // 获取 API 地址 pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName); pByte = (PBYTE)pFunc; // 若已脱钩,则返回False if( pByte[0] != 0xE9 ) return FALSE; // 向内存添加“写”的属性,为恢复原代码做准备 VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); // Unhook memcpy(pFunc, pOrgBytes, 5); // 恢复内存属性 VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect); return TRUE; } // 勾取过程 NTSTATUS WINAPI NewZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) { NTSTATUS status; FARPROC pFunc; PSYSTEM_PROCESS_INFORMATION pCur, pPrev; char szProcName[MAX_PATH] = {0,}; // 开始前先“脱钩” unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes); // 调用原始API pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL), DEF_ZWQUERYSYSTEMINFORMATION); status = ((PFZWQUERYSYSTEMINFORMATION)pFunc) (SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); if( status != STATUS_SUCCESS ) goto __NTQUERYSYSTEMINFORMATION_END; // 针对 SystemProcessInformation 类型操作 if( SystemInformationClass == SystemProcessInformation ) { // SYSTEM_PROCESS_INFORMATION 类型转换 // pCur 是单向链表的头 pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; while(TRUE) { // 比较进程名称 // g_szProcName为要隐藏的进程的名称 // (=在SetProcName()设置) if(pCur->Reserved2[1] != NULL) { if(!_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName)) { // 从链表中删除隐藏进程的节点 if(pCur->NextEntryOffset == 0) pPrev->NextEntryOffset = 0; else pPrev->NextEntryOffset += pCur->NextEntryOffset; } else pPrev = pCur; } if(pCur->NextEntryOffset == 0) break; // 链表的下一项 pCur = (PSYSTEM_PROCESS_INFORMATION) ((ULONG)pCur + pCur->NextEntryOffset); } } __NTQUERYSYSTEMINFORMATION_END: // 函数终止前,再次执行API勾取操作,为下次调用准备 hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, (PROC)NewZwQuerySystemInformation, g_pOrgBytes); return status; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { char szCurProc[MAX_PATH] = {0,}; char *p = NULL; // #1. 异常处理 // 若当前进程为 HookProc.exe 则不进行钩取 GetModuleFileNameA(NULL, szCurProc, MAX_PATH); p = strrchr(szCurProc, '\\'); if( (p != NULL) && !_stricmp(p+1, "HideProc.exe") ) return TRUE; switch( fdwReason ) { // #2. API Hooking case DLL_PROCESS_ATTACH : hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, (PROC)NewZwQuerySystemInformation, g_pOrgBytes); break; // #3. API Unhooking case DLL_PROCESS_DETACH : unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes); break; } return TRUE; } #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) void SetProcName(LPCTSTR szProcName) { _tcscpy_s(g_szProcName, szProcName); } #ifdef __cplusplus } #endif 五、全局API钩取全局 API 钩取针对所有进程,包括正在运行和将要运行的进程 用全局API钩取对上一节内容进行加强 1、相关API (1)Kernel32.CreateProcess()Kernel32.CreateProcess() API 是用来创建新进程的,钩取时要注意: 要同时钩取Kernel32.CreateProcessA()和Kernel32.CreateProcessW()上两个API分别调用了CreateProcessInternalA()和CreateProcessInternalW(),故这两个API也要钩取 BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation );可参考CreateProcess函数详解 (2)Ntdll.ZwResumeThread()Ntdll.ZwResumeThread()在进程创建后,主线程运行前在Kernel32.CreateProcess() 内被调用执行,不过这个API似乎并未公开,故有不确定性 ZwResumeThread( IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL ) 2、源码 (1)HideProc2.exeHideProc2.exe与HideProc.exe相似 // HideProc2.exe #include "windows.h" #include "stdio.h" #include "tlhelp32.h" #include "tchar.h" enum {INJECTION_MODE = 0, EJECTION_MODE}; BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; HANDLE hToken; LUID luid; if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ) { printf("OpenProcessToken error: %u\n", GetLastError()); return FALSE; } if( !LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid) ) // receives LUID of privilege { printf("LookupPrivilegeValue error: %u\n", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if( bEnablePrivilege ) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if( !AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); return FALSE; } if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ) { printf("The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath) { HANDLE hProcess, hThread; LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR); LPTHREAD_START_ROUTINE pThreadProc; if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { printf("OpenProcess(%d) failed!!!\n", dwPID); return FALSE; } pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL); pThreadProc = (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath) { BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) ) return FALSE; bMore = Module32First(hSnapshot, &me); for( ; bMore ; bMore = Module32Next(hSnapshot, &me) ) { if( !_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath) ) { bFound = TRUE; break; } } if( !bFound ) { CloseHandle(hSnapshot); return FALSE; } if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { printf("OpenProcess(%d) failed!!!\n", dwPID); CloseHandle(hSnapshot); return FALSE; } pThreadProc = (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE; } BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath) { DWORD dwPID = 0; HANDLE hSnapShot = INVALID_HANDLE_VALUE; PROCESSENTRY32 pe; // Get the snapshot of the system pe.dwSize = sizeof( PROCESSENTRY32 ); hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL ); // find process Process32First(hSnapShot, &pe); do { dwPID = pe.th32ProcessID; if( dwPID printf("\n Usage : HideProc2.exe \n\n"); return 1; } // change privilege SetPrivilege(SE_DEBUG_NAME, TRUE); // Inject(Eject) Dll to all process if( !_tcsicmp(argv[1], L"-show") ) nMode = EJECTION_MODE; InjectAllProcess(nMode, argv[2]); return 0; } (2)stealth2.dll使用时先将stealth2.dll复制到%SYSTEM%文件夹 #include "windows.h" #include "stdio.h" #include "tchar.h" #define STR_MODULE_NAME (L"stealth2.dll") #define STR_HIDE_PROCESS_NAME (L"notepad.exe") #define STATUS_SUCCESS (0x00000000L) typedef LONG NTSTATUS; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 } SYSTEM_INFORMATION_CLASS; typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; BYTE Reserved1[52]; PVOID Reserved2[3]; HANDLE UniqueProcessId; PVOID Reserved3; ULONG HandleCount; BYTE Reserved4[4]; PVOID Reserved5[11]; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved6[6]; } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); typedef BOOL (WINAPI *PFCREATEPROCESSA)( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); typedef BOOL (WINAPI *PFCREATEPROCESSW)( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); BYTE g_pOrgCPA[5] = {0,}; BYTE g_pOrgCPW[5] = {0,}; BYTE g_pOrgZwQSI[5] = {0,}; BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes) { FARPROC pFunc; DWORD dwOldProtect, dwAddress; BYTE pBuf[5] = {0xE9, 0, }; PBYTE pByte; pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName); pByte = (PBYTE)pFunc; if( pByte[0] == 0xE9 ) return FALSE; VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); memcpy(pOrgBytes, pFunc, 5); dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5; memcpy(&pBuf[1], &dwAddress, 4); memcpy(pFunc, pBuf, 5); VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect); return TRUE; } BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes) { FARPROC pFunc; DWORD dwOldProtect; PBYTE pByte; pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName); pByte = (PBYTE)pFunc; if( pByte[0] != 0xE9 ) return FALSE; VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); memcpy(pFunc, pOrgBytes, 5); VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect); return TRUE; } BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; HANDLE hToken; LUID luid; if( !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) ) { printf("OpenProcessToken error: %u\n", GetLastError()); return FALSE; } if( !LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid) ) // receives LUID of privilege { printf("LookupPrivilegeValue error: %u\n", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if( bEnablePrivilege ) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if( !AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); return FALSE; } if( GetLastError() == ERROR_NOT_ALL_ASSIGNED ) { printf("The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } // 这个InjectDll2值得注意下 BOOL InjectDll2(HANDLE hProcess, LPCTSTR szDllName) { HANDLE hThread; LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR); FARPROC pThreadProc; pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); if( pRemoteBuf == NULL ) return FALSE; WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL); pThreadProc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW"); hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hThread); return TRUE; } NTSTATUS WINAPI NewZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) { NTSTATUS status; FARPROC pFunc; PSYSTEM_PROCESS_INFORMATION pCur, pPrev; char szProcName[MAX_PATH] = {0,}; unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI); pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQuerySystemInformation"); status = ((PFZWQUERYSYSTEMINFORMATION)pFunc) (SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); if( status != STATUS_SUCCESS ) goto __NTQUERYSYSTEMINFORMATION_END; if( SystemInformationClass == SystemProcessInformation ) { pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; while(TRUE) { if(pCur->Reserved2[1] != NULL) { if(!_tcsicmp((PWSTR)pCur->Reserved2[1], STR_HIDE_PROCESS_NAME)) { if(pCur->NextEntryOffset == 0) pPrev->NextEntryOffset = 0; else pPrev->NextEntryOffset += pCur->NextEntryOffset; } else pPrev = pCur; } if(pCur->NextEntryOffset == 0) break; pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset); } } __NTQUERYSYSTEMINFORMATION_END: hook_by_code("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI); return status; } BOOL WINAPI NewCreateProcessA( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { BOOL bRet; FARPROC pFunc; // unhook unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA); // 调用 original API pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA"); bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); // 向生成的子进程注入stealth2.dll if( bRet ) InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME); // hook hook_by_code("kernel32.dll", "CreateProcessA", (PROC)NewCreateProcessA, g_pOrgCPA); return bRet; } BOOL WINAPI NewCreateProcessW( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { BOOL bRet; FARPROC pFunc; // unhook unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW); // 调用 original API pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW"); bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); // 向生成的子进程注入 stealth2.dll if( bRet ) InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME); // hook hook_by_code("kernel32.dll", "CreateProcessW", (PROC)NewCreateProcessW, g_pOrgCPW); return bRet; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { char szCurProc[MAX_PATH] = {0,}; char *p = NULL; // 异常处理使注入不会发生在 HideProc2.exe 进程 GetModuleFileNameA(NULL, szCurProc, MAX_PATH); p = strrchr(szCurProc, '\\'); if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") ) return TRUE; // change privilege SetPrivilege(SE_DEBUG_NAME, TRUE); switch( fdwReason ) { case DLL_PROCESS_ATTACH : // hook hook_by_code("kernel32.dll", "CreateProcessA", (PROC)NewCreateProcessA, g_pOrgCPA); hook_by_code("kernel32.dll", "CreateProcessW", (PROC)NewCreateProcessW, g_pOrgCPW); hook_by_code("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI); break; case DLL_PROCESS_DETACH : // unhook unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA); unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW); unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI); break; } return TRUE; } 结语本章对API钩取做了学习 书中还提点了下工具的使用标准 |
今日新闻 |
推荐新闻 |
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 |